﻿//-----------------------------------------------------------------------
// <copyright file="IMergeCellInternal.cs" company="LMT">
//     Copyright (c) by La Minh Thuận.
// </copyright>
// <author>thuanla1985@hotmail.com</author>
//-----------------------------------------------------------------------
namespace LMT.Forms
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    using LMT.Forms.Text;
    
    /// <summary>
    /// A MergeGridView control has a built in support merge cell and header
    /// </summary>
    public partial class MergeGridView : DataGridView
    {
        /// <summary>
        /// detect wherether user dragging on header cell
        /// </summary>
        private bool isHeaderDragingSize;

        /// <summary>
        /// detect wherether user click on header cell
        /// </summary>
        private bool isHeaderMouseDown;

        /// <summary>
        /// detect wherether user change the value of cell.
        /// </summary>
        private bool isValueChanged;

        /// <summary>
        /// dedicate whether the control has been loaded into memory and showed on screen.
        /// </summary>
        private bool isLoaded;

        /// <summary>
        /// Pen color for grid.
        /// </summary>
        private Pen gridPen;

        /// <summary>
        /// Initializes a new instance of the <see cref="MergeGridView"/> class.
        /// </summary>
        public MergeGridView()
        {
            this.DoubleBuffered = true;
            this.MultiSelect = true;
            this.gridPen = new Pen(this.GridColor, 1.0F);
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is column top border.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is column top border; otherwise, <c>false</c>.
        /// </value>
        public bool IsColumnTopBorder { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is column left border.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is column left border; otherwise, <c>false</c>.
        /// </value>
        public bool IsColumnLeftBorder { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is column bottom border.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is column bottom border; otherwise, <c>false</c>.
        /// </value>
        public bool IsColumnBottomBorder { get; set; }

        /// <summary>
        /// Occurs when [cell horizontal merging].
        /// </summary>
        public event EventHandler<CellMergeEventArgs> CellHorizontalMerging;

        /// <summary>
        /// Occurs when [cell vertical merging].
        /// </summary>
        public event EventHandler<CellMergeEventArgs> CellVerticalMerging;

        /// <summary>
        /// Occurs when [cell both merging].
        /// </summary>
        public event EventHandler<CellMergeEventArgs> CellBothMerging;

        /// <summary>
        /// Gets the grid pen.
        /// </summary>
        /// <value>The grid pen.</value>
        public Pen GridPen
        {
            get { return this.gridPen; }
        }

        /// <summary>
        /// Gets the columns X.
        /// </summary>
        /// <value>The columns X.</value>
        public MergeGridViewColumnCollection ColumnsX
        {
            get { return (MergeGridViewColumnCollection)this.Columns; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [use cell state root event].
        /// </summary>
        /// <value>
        ///  <c>true</c> if [use cell state root event]; otherwise, <c>false</c>.
        /// </value>
        protected bool UseCellStateRootEvent { get; set; }

        /// <summary>
        /// Forces the control to invalidate its client area and immediately redraw itself and any child controls.
        /// </summary>
        /// <PermissionSet>
        ///   <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///   <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///   <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
        ///   <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///   </PermissionSet>
        public void ResetColumnHeader()
        {
            if (this.ColumnsX != null)
            {
                foreach (MergeGridViewColumn mergeColumn in this.ColumnsX)
                {
                    ((MergeGridViewCellHeader)mergeColumn.HeaderCell).IsInitDraw = false;
                }
            }

            this.Refresh();
        }

        /// <summary>
        /// Finds the index of the cell merge column.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columnIndex">Index of the column.</param>
        /// <returns>
        /// List<![CDATA[<int>]]> contains columns index of merge cells
        /// </returns>
        public List<int> FindMergeColumnsIndex(int rowIndex, int columnIndex)
        {
            MergeGridViewTextBoxCell cell = (MergeGridViewTextBoxCell)this.Rows[rowIndex].Cells[columnIndex];

            // if it's a last column of merge block
            if (cell.IsHMergePrevious && !cell.IsHMergeNext)
            {
                return this.FindMergeHorizontalDisplayCell(cell, false);
            }
            else if (cell.IsHMergeNext && !cell.IsHMergePrevious)
            {
                return this.FindMergeHorizontalDisplayCell(cell, true);
            }
            else if (cell.IsHMergeNext && cell.IsHMergePrevious)
            {
                // travel to the left
                while (cell.IsHMergePrevious && this[cell.ColumnIndex - 1, cell.RowIndex].Displayed)
                {
                    cell = (MergeGridViewTextBoxCell)this[cell.ColumnIndex - 1, cell.RowIndex];
                }

                return this.FindMergeHorizontalDisplayCell(cell, true);
            }

            return null;
        }

        /// <summary>
        /// Finds the index of the cell merge column.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columnIndex">Index of the column.</param>
        /// <returns>List<![CDATA[<int>]]> contains rows index of merge cells</returns>
        public List<int> FindMergeRowsDisplayIndex(int rowIndex, int columnIndex)
        {
            MergeGridViewTextBoxCell cell = (MergeGridViewTextBoxCell)this.Rows[rowIndex].Cells[columnIndex];

            // if it's a last row of merge block
            if (cell.IsVMergePrevious && !cell.IsVMergeNext)
            {
                return this.FindMergeVerticalDisplayCells(cell, false);
            }
            else if (cell.IsVMergeNext && !cell.IsVMergePrevious)
            {
                return this.FindMergeVerticalDisplayCells(cell, true);
            }
            else if (cell.IsVMergeNext && cell.IsVMergePrevious)
            {
                // travel to the top
                while (cell.IsVMergePrevious && this[cell.ColumnIndex, cell.RowIndex - 1].Displayed)
                {
                    cell = (MergeGridViewTextBoxCell)this[cell.ColumnIndex, cell.RowIndex - 1];
                }

                return this.FindMergeVerticalDisplayCells(cell, true);
            }

            return null;
        }

        /// <summary>
        /// Assigns the vertical merge selection.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="colIndex">Index of the col.</param>
        /// <param name="select">if set to <c>true</c> [select].</param>
        public void SetCellsSelection(int rowIndex, int colIndex, bool select)
        {
            if (this.ColumnsX[colIndex].MergeDirection != MergeDirection.None)
            {
                if (this.ColumnsX[colIndex].MergeDirection == MergeDirection.Vertical)
                {
                    this.SetVerticalSelection(rowIndex, colIndex, select);
                }
                else if (this.ColumnsX[colIndex].MergeDirection == MergeDirection.Horizontal)
                {
                    this.SetHorizontalSelection(rowIndex, colIndex, select);
                }
                else if (this.ColumnsX[colIndex].MergeDirection == MergeDirection.Both)
                {
                    this.SetBothWaySelection(rowIndex, colIndex, select);
                }
            }
        }

        /// <summary>
        /// Finds the display merge rows area.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columnIndex">Index of the column.</param>
        /// <returns>Rectangle of merge area</returns>
        internal Rectangle FindMergeRowsDisplayArea(int rowIndex, int columnIndex)
        {
            List<int> mergeIndexs;
            return this.FindMergeRowsDisplayArea(rowIndex, columnIndex, out mergeIndexs);
        }

        /// <summary>
        /// Finds the vertical editing panel area.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columnIndex">Index of the column.</param>
        /// <param name="displayIndex">The display index.</param>
        /// <returns>
        /// Rectangle of merge area
        /// </returns>
        internal Rectangle FindMergeRowsDisplayArea(int rowIndex, int columnIndex, out List<int> displayIndex)
        {
            Rectangle mergeRect = Rectangle.Empty;
            displayIndex = this.FindMergeRowsDisplayIndex(rowIndex, columnIndex);

            // Move panel to the first cell of merge block
            if (displayIndex != null)
            {
                mergeRect = this.GetCellDisplayRectangle(columnIndex, displayIndex[0], false);

                // If this column cell is first of display and it hidden.
                if (columnIndex == this.FirstDisplayedCell.ColumnIndex && this.FirstDisplayedScrollingColumnHiddenWidth > 1)
                {
                    mergeRect.X -= this.FirstDisplayedScrollingColumnHiddenWidth;
                    mergeRect.Width += this.FirstDisplayedScrollingColumnHiddenWidth;
                }

                // If this is a first column, execute by 1 for border
                if (columnIndex == 0)
                {
                    mergeRect.X--;
                    mergeRect.Width++;
                }

                int runEnd = displayIndex[displayIndex.Count - 1];
                for (int rowI = displayIndex[0] + 1; rowI <= runEnd; rowI++)
                {
                    mergeRect.Height += this.Rows[rowI].Height;
                }
            }

            return mergeRect;
        }

        /// <summary>
        /// Finds the horizontal editing panel area.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columnIndex">Index of the column.</param>
        /// <returns>Rectangle of merge area</returns>
        internal Rectangle FindMergeColumnsDisplayArea(int rowIndex, int columnIndex)
        {
            List<int> mergeIndexs;
            return this.FindMergeColumnsDisplayArea(rowIndex, columnIndex, out mergeIndexs);
        }

        /// <summary>
        /// Finds the horizontal editing panel area.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columnIndex">Index of the column.</param>
        /// <param name="displayIndex">The display index.</param>
        /// <returns>Rectangle of merge area</returns>
        internal Rectangle FindMergeColumnsDisplayArea(int rowIndex, int columnIndex, out List<int> displayIndex)
        {
            Rectangle mergeRect = Rectangle.Empty;
            displayIndex = this.FindMergeColumnsIndex(rowIndex, columnIndex);

            // move editing panel to the first cell of merge block
            if (displayIndex != null)
            {
                mergeRect = this.GetCellDisplayRectangle(displayIndex[0], rowIndex, false);

                // If this column cell is first of display and it hidden.
                if (columnIndex == this.FirstDisplayedCell.ColumnIndex && this.FirstDisplayedScrollingColumnHiddenWidth > 1)
                {
                    mergeRect.X -= this.FirstDisplayedScrollingColumnHiddenWidth;
                    mergeRect.Width += this.FirstDisplayedScrollingColumnHiddenWidth;
                }

                // If this is a first column, execute by 1 for border
                if (columnIndex == 0)
                {
                    mergeRect.X--;
                }

                int runEnd = displayIndex[displayIndex.Count - 1];
                for (int rowI = displayIndex[0] + 1; rowI <= runEnd; rowI++)
                {
                    mergeRect.Width += this.ColumnsX[rowI].Width;
                }
            }

            return mergeRect;
        }

        /// <summary>
        /// Finds the both way editing panel area.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columnIndex">Index of the column.</param>
        /// <returns>Rectangle : display area of merge block</returns>
        internal Rectangle FindMergeBothWayDisplayArea(int rowIndex, int columnIndex)
        {
            List<int> rowsIndex, colsIndex;
            return this.FindMergeBothWayDisplayArea(rowIndex, columnIndex, out rowsIndex, out colsIndex);
        }

        /// <summary>
        /// Finds the both way editing panel area.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columnIndex">Index of the column.</param>
        /// <param name="rowsIndex">Index of the rows.</param>
        /// <param name="colsIndex">Index of the cols.</param>
        /// <returns>
        /// Rectangle : display area of merge block
        /// </returns>
        internal Rectangle FindMergeBothWayDisplayArea(int rowIndex, int columnIndex, out List<int> rowsIndex, out List<int> colsIndex)
        {
            Rectangle mergeRect = Rectangle.Empty;
            Rectangle verticRect = this.FindMergeRowsDisplayArea(rowIndex, columnIndex, out rowsIndex);
            Rectangle horiRect = this.FindMergeColumnsDisplayArea(rowIndex, columnIndex, out colsIndex);

            if (!verticRect.IsEmpty)
            {
                mergeRect = verticRect;
            }

            if (!horiRect.IsEmpty)
            {
                if (!verticRect.IsEmpty)
                {
                    mergeRect.X = horiRect.Left;
                    mergeRect.Width = horiRect.Width;
                }
                else
                {
                    mergeRect = horiRect;
                }
            }

            return mergeRect;
        }

        /// <summary>
        /// Redraws the header.
        /// </summary>
        internal void RedrawCellsHeader()
        {
            MergeGridViewCellHeader cellHeader = null;
            foreach (MergeGridViewColumn col in this.ColumnsX)
            {
                cellHeader = (MergeGridViewCellHeader)col.HeaderCell;
                cellHeader.IsInitDraw = false;
            }
        }

        /// <summary>
        /// Assigns the horizontal selection.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="colIndex">Index of the col.</param>
        /// <param name="selected">if set to <c>true</c> [selected].</param>
        internal void SetHorizontalSelection(int rowIndex, int colIndex, bool selected)
        {
            List<int> mergeCols = this.FindMergeColumnsIndex(rowIndex, colIndex);
            if (mergeCols != null)
            {
                foreach (int column in mergeCols)
                {
                    this.Rows[rowIndex].Cells[column].Selected = selected;
                }
            }
        }

        /// <summary>
        /// Assigns the vertical selection.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="colIndex">Index of the col.</param>
        /// <param name="selected">if set to <c>true</c> [selected].</param>
        internal void SetVerticalSelection(int rowIndex, int colIndex, bool selected)
        {
            List<int> mergeRows = this.FindMergeRowsDisplayIndex(rowIndex, colIndex);
            if (mergeRows != null && this.MultiSelect)
            {
                foreach (int row in mergeRows)
                {
                    this.Rows[row].Cells[colIndex].Selected = selected;
                }
            }
        }

        /// <summary>
        /// Assigns the both merge selection.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="colIndex">Index of the col.</param>
        /// <param name="selected">if set to <c>true</c> [selected].</param>
        internal void SetBothWaySelection(int rowIndex, int colIndex, bool selected)
        {
            MergeGridViewTextBoxCell cell = (MergeGridViewTextBoxCell)this.Rows[rowIndex].Cells[colIndex];

            // travel to the left
            while (cell.IsHMergePrevious)
            {
                cell = (MergeGridViewTextBoxCell)this.Rows[rowIndex].Cells[cell.ColumnIndex - 1];
            }

            // travel to the top
            while (cell.IsVMergePrevious)
            {
                cell = (MergeGridViewTextBoxCell)this.Rows[cell.RowIndex - 1].Cells[cell.ColumnIndex];
            }

            int beginColumnIndex = cell.ColumnIndex;
            int beginRowIndex = cell.RowIndex;

            // Cout horizontal merge
            int horiCount = 1;
            int vertiCount = 1;

            while (cell.IsHMergeNext)
            {
                horiCount++;
                cell = (MergeGridViewTextBoxCell)this.Rows[beginRowIndex].Cells[cell.ColumnIndex + 1];
            }

            while (cell.IsVMergeNext)
            {
                vertiCount++;
                cell = (MergeGridViewTextBoxCell)this.Rows[cell.RowIndex + 1].Cells[beginColumnIndex];
            }

            for (int indexV = 0, rowI = beginRowIndex; indexV < vertiCount; rowI++, indexV++)
            {
                for (int indexH = 0, colI = beginColumnIndex; indexH < horiCount; colI++, indexH++)
                {
                    cell = (MergeGridViewTextBoxCell)this.Rows[rowI].Cells[colI];
                    cell.Selected = selected;
                }
            }
        }

        /// <summary>
        ///  Raises the <see cref="E:System.Windows.Forms.Control.VisibleChanged"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnVisibleChanged(EventArgs e)
        {
            if (!this.isLoaded)
            {
                foreach (MergeGridViewColumn column in this.ColumnsX)
                {
                    if (column.IsFirstSpanning)
                    {
                        column.ApplyMergingLayout();
                    }
                }

                this.isLoaded = true;
            }

            base.OnVisibleChanged(e);
        }

        /// <summary>
        /// Creates and returns a new <see cref="T:System.Windows.Forms.DataGridViewColumnCollection"/>.
        /// </summary>
        /// <returns>
        /// An empty <see cref="T:System.Windows.Forms.DataGridViewColumnCollection"/>.
        /// </returns>
        protected override DataGridViewColumnCollection CreateColumnsInstance()
        {
            return new MergeGridViewColumnCollection(this);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.GridColorChanged"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnGridColorChanged(EventArgs e)
        {
            if (this.gridPen != null)
            {
                this.GridPen.Dispose();
            }

            this.gridPen = new Pen(this.GridColor, 1.0F);
            base.OnGridColorChanged(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.CellValueChanged"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewCellEventArgs"/> that contains the event data.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellEventArgs.ColumnIndex"/> property of <paramref name="e"/> is greater than the number of columns in the control minus one.
        /// -or-
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellEventArgs.RowIndex"/> property of <paramref name="e"/> is greater than the number of rows in the control minus one.
        /// </exception>
        protected override void OnCellValueChanged(DataGridViewCellEventArgs e)
        {
            this.isValueChanged = true;
            base.OnCellValueChanged(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.CellEndEdit"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewCellEventArgs"/> that contains the event data.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellEventArgs.ColumnIndex"/> property of <paramref name="e"/> is greater than the number of columns in the control minus one.
        /// -or-
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellEventArgs.RowIndex"/> property of <paramref name="e"/> is greater than the number of rows in the control minus one.
        /// </exception>
        protected override void OnCellEndEdit(DataGridViewCellEventArgs e)
        {
            if (this.ColumnsX[e.ColumnIndex].MergeDirection != MergeDirection.None && this.isValueChanged)
            {
                ((IMergeCellInternal)this[e.ColumnIndex, e.RowIndex]).IsAnalyzeMerge = false;

                // Edit value for all remain cell if property is on
                if (this.ColumnsX[e.ColumnIndex].IsMergeCellEditAll)
                {
                    this.SetMergeCellValue(e, this.ColumnsX[e.ColumnIndex]);
                }

                this.SuspendLayout();

                // Redraw for specify cells ( Rows & Columns )
                if (this.ColumnsX[e.ColumnIndex].MergeDirection == MergeDirection.Vertical)
                {
                    this.InvalidateColumn(e.ColumnIndex);
                }
                else if (this.ColumnsX[e.ColumnIndex].MergeDirection == MergeDirection.Horizontal)
                {
                    this.InvalidateRow(e.RowIndex);
                }
                else if (this.ColumnsX[e.ColumnIndex].MergeDirection == MergeDirection.Both)
                {
                    this.Invalidate(false);
                }

                this.ResumeLayout(false);
            }

            this.isValueChanged = false;
            base.OnCellEndEdit(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.CellPainting"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> that contains the event data.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellPaintingEventArgs.ColumnIndex"/> property of <paramref name="e"/> is greater than the number of columns in the control minus one.
        /// -or-
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellPaintingEventArgs.RowIndex"/> property of <paramref name="e"/> is greater than the number of rows in the control minus one.
        /// </exception>
        protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
        {
            // we only paint cell content not header
            if (e.RowIndex != -1 && e.ColumnIndex != -1)
            {
                MergeGridViewColumn mergeCol = (MergeGridViewColumn)this.ColumnsX[e.ColumnIndex];
                if (mergeCol.MergeDirection != MergeDirection.None)
                {
                    // Merge vertical cell
                    if (mergeCol.MergeDirection == MergeDirection.Vertical)
                    {
                        if (mergeCol.MergeRule == MergeRule.Content)
                        {
                            this.MergeVerticalContent(e);
                        }
                        else if (mergeCol.MergeRule == MergeRule.Owner)
                        {
                            this.MergeVerticalOwner(e);
                        }
                    }

                    // Merge horizontal cell if cell header is span
                    if (mergeCol.MergeDirection == MergeDirection.Horizontal)
                    {
                        if (mergeCol.MergeRule == MergeRule.Content)
                        {
                            this.MergeHorizontalContent(e);
                        }
                        else if (mergeCol.MergeRule == MergeRule.Owner)
                        {
                            this.MergeHorizontalOwner(e);
                        }
                    }

                    // Merge horizontal cell if cell header is span
                    if (mergeCol.MergeDirection == MergeDirection.Both)
                    {
                        if (mergeCol.MergeRule == MergeRule.Content)
                        {
                            this.MergeBothContent(e);
                        }
                        else if (mergeCol.MergeRule == MergeRule.Owner)
                        {
                            this.MergeBothOwner(e);
                        }
                    }
                }
            }
            
            base.OnCellPainting(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.Scroll"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.ScrollEventArgs"/> that contains the event data.</param>
        protected override void OnScroll(ScrollEventArgs e)
        {
            if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
            {
                this.RedrawCellsHeader();
            }
            else if (e.ScrollOrientation == ScrollOrientation.VerticalScroll)
            {
                this.RedrawDisplayColumns();
            }

            base.OnScroll(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.CellMouseDown"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewCellMouseEventArgs"/> that contains the event data.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellMouseEventArgs.ColumnIndex"/> property of <paramref name="e"/> is greater than the number of columns in the control minus one.
        /// -or-
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellMouseEventArgs.RowIndex"/> property of <paramref name="e"/> is greater than the number of rows in the control minus one.
        /// </exception>
        /// <exception cref="T:System.Exception">
        /// This action would commit a cell value or enter edit mode, but an error in the data source prevents the action and either there is no handler for the <see cref="E:System.Windows.Forms.DataGridView.DataError"/> event or the handler has set the <see cref="P:System.Windows.Forms.DataGridViewDataErrorEventArgs.ThrowException"/> property to true.
        /// </exception>
        protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
        {
            if (e.RowIndex == -1 | e.RowIndex == 0)
            {
                this.isHeaderMouseDown = true;
            }

            base.OnCellMouseDown(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.CellMouseMove"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewCellMouseEventArgs"/> that contains the event data.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellMouseEventArgs.ColumnIndex"/> property of <paramref name="e"/> is greater than the number of columns in the control minus one.
        /// -or-
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellMouseEventArgs.RowIndex"/> property of <paramref name="e"/> is greater than the number of rows in the control minus one.
        /// </exception>
        protected override void OnCellMouseMove(DataGridViewCellMouseEventArgs e)
        {
            if (this.isHeaderMouseDown)
            {
                this.isHeaderDragingSize = true;
            }

            base.OnCellMouseMove(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.CellMouseUp"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewCellMouseEventArgs"/> that contains the event data.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellMouseEventArgs.ColumnIndex"/> property of <paramref name="e"/> is greater than the number of columns in the control minus one.
        /// -or-
        /// The value of the <see cref="P:System.Windows.Forms.DataGridViewCellMouseEventArgs.RowIndex"/> property of <paramref name="e"/> is greater than the number of rows in the control minus one.
        /// </exception>
        protected override void OnCellMouseUp(DataGridViewCellMouseEventArgs e)
        {
            if (this.isHeaderDragingSize)
            {
                this.RedrawCellsHeader();
            }

            this.isHeaderDragingSize = false;
            this.isHeaderMouseDown = false;
            base.OnCellMouseUp(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.ColumnAdded"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewColumnEventArgs"/> that contains the event data.</param>
        /// <exception cref="T:System.ArgumentException">
        /// The column indicated by the <see cref="P:System.Windows.Forms.DataGridViewColumnEventArgs.Column"/> property of <paramref name="e"/> does not belong to this <see cref="T:System.Windows.Forms.DataGridView"/> control.
        /// </exception>
        protected override void OnColumnAdded(DataGridViewColumnEventArgs e)
        {
            if (!(e.Column is MergeGridViewColumn))
            {
                throw new ArgumentException("MergeGridView can only contain MergeGridViewColumn");
            }

            base.OnColumnAdded(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.CellStateChanged"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewCellStateChangedEventArgs"/> that contains the event data.</param>
        protected override void OnCellStateChanged(DataGridViewCellStateChangedEventArgs e)
        {
            if (this.SelectionMode == DataGridViewSelectionMode.CellSelect && !this.UseCellStateRootEvent)
            {
                // peding draw layout for multiselect
                this.SuspendLayout();

                this.SetCellsSelection(e.Cell.RowIndex, e.Cell.ColumnIndex, e.Cell.Selected);

                // resume draw layout for multiselect
                this.ResumeLayout(false);
            }

            base.OnCellStateChanged(e);
        }
        
        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.DataBindingComplete"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewBindingCompleteEventArgs"/> that contains the event data.</param>
        protected override void OnDataBindingComplete(DataGridViewBindingCompleteEventArgs e)
        {
            // Unselect default cell
            this.ClearSelection();
            base.OnDataBindingComplete(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.DataGridView.RowHeightChanged"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DataGridViewRowEventArgs"/> that contains the event data.</param>
        /// <exception cref="T:System.ArgumentException">
        /// The row indicated by the <see cref="P:System.Windows.Forms.DataGridViewRowEventArgs.Row"/> property of <paramref name="e"/> does not belong to this <see cref="T:System.Windows.Forms.DataGridView"/> control.
        /// </exception>
        protected override void OnRowHeightChanged(DataGridViewRowEventArgs e)
        {
            base.OnRowHeightChanged(e);
            this.RedrawDisplayColumns();
        }

        /// <summary>
        /// Raises the <see cref="E:MergeVerticalPaintBackground"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        /// <param name="ce">The <see cref="LMT.Forms.CellMergeEventArgs"/> instance containing the event data.</param>
        protected virtual void OnMergeVerticalPaint(DataGridViewCellPaintingEventArgs e, CellMergeEventArgs ce)
        {
            List<int> mergeCells;
            SolidBrush backClr = new SolidBrush(e.CellStyle.BackColor);
            Rectangle bounds = this.FindMergeRowsDisplayArea(e.RowIndex, e.ColumnIndex, out mergeCells);

            // Clear background of all merge cell
            e.Graphics.FillRectangle(backClr, bounds);

            this.FillSelectionMergeVerticalCells(e, backClr, mergeCells);

            // Draw left & right border
            e.Graphics.DrawLine(this.gridPen, bounds.Left - (ce.IsFirstColumn ? 0 : 1), bounds.Top, bounds.Left - (ce.IsFirstColumn ? 0 : 1), bounds.Bottom - 1);
            e.Graphics.DrawLine(this.gridPen, bounds.Right - 1, bounds.Top, bounds.Right - 1, bounds.Bottom - 1);

            // If next row cell has not merge anymore, draw the border between us.
            if (!ce.IsVMergeNext)
            {
                e.Graphics.DrawLine(this.gridPen, bounds.Left, bounds.Bottom - 1, bounds.Right, bounds.Bottom - 1);
            }

            backClr.Dispose();

            if (e.Value != null)
            {
                StringFormat sF = Layouts.ParseAlignment(e.CellStyle.Alignment);
                Brush foreG = new SolidBrush(e.CellStyle.ForeColor);
                e.Graphics.DrawString(e.Value.ToString(), e.CellStyle.Font, foreG, bounds, sF);

                // release managed resource
                sF.Dispose();
                foreG.Dispose();
            }
        }

        /// <summary>
        /// Raises the <see cref="E:MergeHorizontalPaintBackground"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        /// <param name="ce">The <see cref="LMT.Forms.CellMergeEventArgs"/> instance containing the event data.</param>
        protected virtual void OnMergeHorizontalPaint(DataGridViewCellPaintingEventArgs e, CellMergeEventArgs ce)
        {
            List<int> mergeCols;
            Rectangle bounds = this.FindMergeColumnsDisplayArea(e.RowIndex, e.ColumnIndex, out mergeCols);
            SolidBrush backClr = new SolidBrush(e.CellStyle.BackColor);

            e.Graphics.FillRectangle(backClr, bounds);

            this.FillSelectionMergeHorizontalCells(e, backClr, mergeCols);

            // draw right & bottom border
            e.Graphics.DrawLine(this.GridPen, bounds.Left, bounds.Bottom - 1, bounds.Right, bounds.Bottom - 1);

            if (!ce.IsHMergeNext)
            {
                e.Graphics.DrawLine(this.GridPen, bounds.Right - 1, bounds.Top, bounds.Right - 1, bounds.Bottom - 1);
            }

            backClr.Dispose();

            if (e.Value != null)
            {
                StringFormat sF = Layouts.ParseAlignment(e.CellStyle.Alignment);
                Brush foreG = new SolidBrush(this[e.ColumnIndex, e.RowIndex].Selected ? e.CellStyle.SelectionForeColor : e.CellStyle.ForeColor);
                e.Graphics.DrawString(e.Value.ToString(), e.CellStyle.Font, foreG, bounds, sF);

                // release managed resource
                sF.Dispose();
                foreG.Dispose();
            }
        }

        /// <summary>
        /// Raises the <see cref="E:MergeBothWayPaint"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        /// <param name="ce">The <see cref="LMT.Forms.CellMergeEventArgs"/> instance containing the event data.</param>
        protected virtual void OnMergeBothWayPaint(DataGridViewCellPaintingEventArgs e, CellMergeEventArgs ce)
        {
            List<int> mergeRows, mergeCols;
            Rectangle bounds = this.FindMergeBothWayDisplayArea(e.RowIndex, e.ColumnIndex, out mergeRows, out mergeCols);
            SolidBrush backClr = new SolidBrush(e.CellStyle.BackColor);

            // Clear all merge cells
            e.Graphics.FillRectangle(backClr, bounds);

            if (mergeRows != null && mergeCols == null)
            {
                this.FillSelectionMergeVerticalCells(e, backClr, mergeRows);
            }
            else if (mergeCols != null && mergeRows == null)
            {
                this.FillSelectionMergeHorizontalCells(e, backClr, mergeCols);
            }
            else if (mergeCols != null && mergeRows != null)
            {
                this.FillSelectionMergeBothCells(e, backClr, mergeRows, mergeCols);
            }

            backClr.Dispose();

            // draw right border
            e.Graphics.DrawLine(this.GridPen, bounds.Right - 1, bounds.Top, bounds.Right - 1, bounds.Bottom - 1);

            // If next row cell has not merge anymore, draw the border between us.
            if (!ce.IsVMergeNext)
            {
                e.Graphics.DrawLine(this.GridPen, bounds.Left, bounds.Bottom - 1, bounds.Right, bounds.Bottom - 1);
            }

            if (e.Value != null)
            {
                StringFormat sF = Layouts.ParseAlignment(e.CellStyle.Alignment);
                Brush foreG = new SolidBrush(e.CellStyle.ForeColor);
                e.Graphics.DrawString(e.Value.ToString(), e.CellStyle.Font, foreG, bounds, sF);

                // release managed resource
                sF.Dispose();
                foreG.Dispose();
            }
        }

        /// <summary>
        /// Release both managed and unmanaged resources
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            if (this.GridPen != null)
            {
                this.gridPen.Dispose();
            }

            base.Dispose(disposing);
        }

        /// <summary>
        /// Analyzes the vertical merge.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columIndex">Index of the colum.</param>
        /// <param name="value">The value.</param>
        private void AnalyzeVerticalMerge(int rowIndex, int columIndex, object value)
        {
            // Find the top of merge cells
            while (rowIndex > 0 && 
                this[columIndex, rowIndex - 1].Displayed && 
                this[columIndex, rowIndex - 1].Value.Equals(value))
            {
                if (((IMergeCellInternal)this[columIndex, rowIndex - 1]).IsAnalyzeMerge)
                {
                    break;
                }

                rowIndex--;
            }

            // Only analyze rows is visibled.
            int runEnd = this.FirstDisplayedCell.RowIndex + this.DisplayedRowCount(true);

            // Compare content of each row cell
            IMergeCellInternal cell = (IMergeCellInternal)this[columIndex, rowIndex];
            for (; rowIndex < runEnd; rowIndex++)
            {
                cell = (IMergeCellInternal)this[columIndex, rowIndex];
                cell.IsAnalyzeMerge = true;

                // First doesn't merge with any previous row.
                if (rowIndex > 0 && this[columIndex, rowIndex - 1].Value.Equals(value))
                {
                    cell.IsVMergePrevious = true;
                }

                // Last row doesn't merge with any next row.
                if (rowIndex < this.Rows.Count - 1 && this[columIndex, rowIndex + 1].Value.Equals(value))
                {
                    cell.IsVMergeNext = true;
                }
                else
                {
                    break;
                }
            }
        }

        /// <summary>
        /// Analyzes the horizontal merge.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columIndex">Index of the colum.</param>
        /// <param name="value">The value.</param>
        private void AnalyzeHorizontalMerge(int rowIndex, int columIndex, object value)
        {
            // Find the left of merge cells
            while (columIndex > 0 && 
                this[columIndex - 1, rowIndex].Displayed && 
                this[columIndex - 1, rowIndex].Value.Equals(value))
            {
                if (((IMergeCellInternal)this[columIndex - 1, rowIndex]).IsAnalyzeMerge)
                {
                    break;
                }

                columIndex--;
            }

            // Only analyze columns is visibled.
            int runEnd = this.FirstDisplayedCell.ColumnIndex + this.DisplayedColumnCount(true);

            // Compare content of each cell
            IMergeCellInternal cell = (IMergeCellInternal)this[columIndex, rowIndex];
            for (; columIndex < runEnd; columIndex++)
            {
                if (this.ColumnsX[columIndex].MergeDirection != MergeDirection.Horizontal)
                {
                    break;
                }

                cell = (IMergeCellInternal)this[columIndex, rowIndex];
                cell.IsAnalyzeMerge = true;

                // Last row doesn't merge with any next row.
                if (columIndex < this.Rows.Count - 1 &&
                    this.ColumnsX[columIndex + 1].MergeDirection == MergeDirection.Horizontal &&
                    this[columIndex + 1, rowIndex].Value.Equals(value))
                {
                    cell.IsHMergeNext = true;
                }

                // First doesn't merge with any previous row.
                if (columIndex > 0 &&
                    this.ColumnsX[columIndex - 1].MergeDirection == MergeDirection.Horizontal &&
                    this[columIndex - 1, rowIndex].Value.Equals(value))
                {
                    cell.IsHMergePrevious = true;
                }
            }
        }

        /// <summary>
        /// Analyzes the both merge.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="columIndex">Index of the colum.</param>
        /// <param name="value">The value.</param>
        private void AnalyzeBothMerge(int rowIndex, int columIndex, object value)
        {
            // Find the left of merge cells
            while (columIndex > 0 && 
                this[columIndex - 1, rowIndex].Displayed &&
                this.ColumnsX[columIndex].MergeDirection == MergeDirection.Both && 
                this[columIndex - 1, rowIndex].Value.Equals(value))
            {
                columIndex--;
            }

            // Find the top of merge cells
            while (rowIndex > 0 &&
                this[columIndex, rowIndex - 1].Displayed &&
                this[columIndex, rowIndex - 1].Value.Equals(value))
            {
                rowIndex--;
            }

            // Compare content of each row cell
            IMergeCellInternal cell = (IMergeCellInternal)this[columIndex, rowIndex];
            bool vertiNextMerge;

            for (; rowIndex < this.Rows.Count; rowIndex++)
            {
                // Last row doesn't merge next with any row.
                vertiNextMerge = rowIndex < this.Rows.Count - 1 ? this[columIndex, rowIndex + 1].Value.Equals(value) : false;
                for (int colIndex = columIndex; colIndex < this.ColumnsX.Count; colIndex++)
                {
                    if (this.ColumnsX[colIndex].MergeDirection != MergeDirection.Both)
                    {
                        break;
                    }

                    cell = (IMergeCellInternal)this[colIndex, rowIndex];
                    cell.IsAnalyzeMerge = true;

                    if (colIndex > 0 && 
                        this.ColumnsX[colIndex - 1].MergeDirection == MergeDirection.Both &&
                        this[colIndex - 1, rowIndex].Value.Equals(value))
                    {
                        cell.IsHMergePrevious = true;
                    }

                    if (rowIndex > 0 && this[colIndex, rowIndex - 1].Value.Equals(value))
                    {
                        cell.IsVMergePrevious = true;
                    }

                    cell.IsVMergeNext = vertiNextMerge;

                    // The last comlumn &
                    if (colIndex == this.ColumnsX.Count - 1)
                    {
                        break;
                    }

                    if (this.ColumnsX[colIndex + 1].MergeDirection != MergeDirection.Both |
                        !this[colIndex + 1, rowIndex].Value.Equals(value))
                    {
                        break;
                    }

                    cell.IsHMergeNext = true;
                }

                if (!vertiNextMerge)
                {
                    break;
                }
            }
        }

        /// <summary>
        /// Redraws all columns has been merge only.
        /// </summary>
        private void RedrawDisplayColumns()
        {
            foreach (MergeGridViewColumn col in this.ColumnsX)
            {
                if (col.Displayed && col.MergeDirection != MergeDirection.None)
                {
                    this.InvalidateColumn(col.DisplayIndex);
                }
            }
        }

        /// <summary>
        /// Find the merge cell by direction.
        /// </summary>
        /// <param name="cell">The cell at the top or bottom of merge block.</param>
        /// <param name="isTopDown">if set to <c>true</c> [is top down].</param>
        /// <returns>
        /// List<![CDATA[<int>]]> contains rows index of merge cells
        /// </returns>
        private List<int> FindMergeVerticalDisplayCells(MergeGridViewTextBoxCell cell, bool isTopDown)
        {
            List<int> rowsIndex = new List<int>();

            if (isTopDown)
            {
                // if it's a begin row of merge block
                rowsIndex.Add(cell.RowIndex);
                while (cell.IsVMergeNext && this[cell.ColumnIndex, cell.RowIndex + 1].Displayed)
                {   // travel to bottom
                    cell = (MergeGridViewTextBoxCell)this[cell.ColumnIndex, cell.RowIndex + 1];
                    rowsIndex.Add(cell.RowIndex);
                }
            }
            else
            {
                rowsIndex.Add(cell.RowIndex);
                while (cell.IsVMergePrevious && this[cell.ColumnIndex, cell.RowIndex - 1].Displayed)
                {   // travel to the top
                    cell = (MergeGridViewTextBoxCell)this[cell.ColumnIndex, cell.RowIndex - 1];
                    rowsIndex.Add(cell.RowIndex);
                }

                rowsIndex.Sort();
            }

            return rowsIndex;
        }

        /// <summary>
        /// Finds the merge cell by left right.
        /// </summary>
        /// <param name="cell">The cell at the left or right hand of merge block</param>
        /// <param name="isLeftRight">if set to <c>true</c> [is left right].</param>
        /// <returns>
        /// List<![CDATA[<int>]]> contains column index of merge cells
        /// </returns>
        private List<int> FindMergeHorizontalDisplayCell(MergeGridViewTextBoxCell cell, bool isLeftRight)
        {
            List<int> rowsIndex = new List<int>();

            if (isLeftRight)
            {
                // if it's a begin row of merge block
                rowsIndex.Add(cell.ColumnIndex);
                while (cell.IsHMergeNext && this[cell.ColumnIndex + 1, cell.RowIndex].Displayed)
                {   // travel to bottom
                    cell = (MergeGridViewTextBoxCell)this[cell.ColumnIndex + 1, cell.RowIndex];
                    rowsIndex.Add(cell.ColumnIndex);
                }
            }
            else
            {
                rowsIndex.Add(cell.ColumnIndex);
                while (cell.IsHMergePrevious && this[cell.ColumnIndex - 1, cell.RowIndex].Displayed)
                {   // travel to the top
                    cell = (MergeGridViewTextBoxCell)this[cell.ColumnIndex - 1, cell.RowIndex];
                    rowsIndex.Add(cell.ColumnIndex);
                }

                rowsIndex.Sort();
            }

            return rowsIndex;
        }

        /// <summary>
        /// Draws the merge vertical column.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        private void MergeVerticalContent(DataGridViewCellPaintingEventArgs e)
        {
            CellMergeEventArgs cellMg = new CellMergeEventArgs(e.ColumnIndex, e.RowIndex);

            // The last row of grid
            if (e.RowIndex == this.Rows.Count - 1)
            {
                cellMg.IsLastRow = true;
            }

            // The last row of grid
            if (e.ColumnIndex == this.ColumnsX.Count - 1)
            {
                cellMg.IsLastColumn = true;
            }

            // Analyze merge of previous & next rows
            if (!(this[e.ColumnIndex, e.RowIndex] as IMergeCellInternal).IsAnalyzeMerge)
            {
                this.AnalyzeVerticalMerge(e.RowIndex, e.ColumnIndex, e.Value);
            }

            IMergeCell cell = (IMergeCell)this[e.ColumnIndex, e.RowIndex];
            cellMg.IsVMergeNext = cell.IsVMergeNext;
            cellMg.IsVMergePrevious = cell.IsVMergePrevious;

            // let's owner draw handle
            if (cell.IsVMergeNext | cell.IsVMergePrevious)
            {
                e.Handled = true;
                this.OnMergeVerticalPaint(e, cellMg);
            }
        }

        /// <summary>
        /// Merges the vertical owner.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        private void MergeVerticalOwner(DataGridViewCellPaintingEventArgs e)
        {
            if (this.CellVerticalMerging != null)
            {
                CellMergeEventArgs cellMg;
                IMergeCellInternal cell;

                // Only analyze rows is visibled.
                int runEnd = this.FirstDisplayedCell.RowIndex + this.DisplayedRowCount(true);
                if (!((IMergeCellInternal)this[e.ColumnIndex, e.RowIndex]).IsAnalyzeMerge)
                {
                    for (int index = e.RowIndex; index < runEnd; index++)
                    {
                        cellMg = new CellMergeEventArgs(e.ColumnIndex, index);

                        // The last row of grid
                        if (index == this.Rows.Count - 1)
                        {
                            cellMg.IsLastRow = true;
                        }

                        // The last row of grid
                        if (e.ColumnIndex == this.ColumnsX.Count - 1)
                        {
                            cellMg.IsLastColumn = true;
                        }

                        // fire an event
                        this.CellVerticalMerging(this, cellMg);

                        // The first row is doesn't merge with any previous
                        if (cellMg.IsFirstRow)
                        {
                            cellMg.IsVMergePrevious = false;
                        }

                        // The last row is doesn't merge with any next
                        if (cellMg.IsLastRow)
                        {
                            cellMg.IsVMergeNext = false;
                        }

                        cell = (IMergeCellInternal)this[e.ColumnIndex, index];
                        cell.IsAnalyzeMerge = true;
                        cell.IsVMergeNext = cellMg.IsVMergeNext;
                        cell.IsVMergePrevious = cellMg.IsVMergePrevious;

                        if (cellMg.IsEndMerge)
                        {
                            break;
                        }
                    }
                }

                cell = (IMergeCellInternal)this[e.ColumnIndex, e.RowIndex];
                cellMg = new CellMergeEventArgs(e.ColumnIndex, e.RowIndex);
                cellMg.IsVMergeNext = cell.IsVMergeNext;
                cellMg.IsVMergePrevious = cell.IsVMergePrevious;

                // let's owner draw handle
                if (cell.IsVMergeNext | cell.IsVMergePrevious)
                {
                    e.Handled = true;
                    this.OnMergeVerticalPaint(e, cellMg);
                }
            }
        }

        /// <summary>
        /// Draws the merge horizontal column.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        private void MergeHorizontalContent(DataGridViewCellPaintingEventArgs e)
        {
            CellMergeEventArgs cellMg = new CellMergeEventArgs(e.ColumnIndex, e.RowIndex);

            // The last row of grid
            if (e.RowIndex == this.Rows.Count - 1)
            {
                cellMg.IsLastRow = true;
            }

            // The last row of grid
            if (e.ColumnIndex == this.ColumnsX.Count - 1)
            {
                cellMg.IsLastColumn = true;
            }

            // Analyze merge of previous & next columns
            if (!(this[e.ColumnIndex, e.RowIndex] as IMergeCellInternal).IsAnalyzeMerge)
            {
                this.AnalyzeHorizontalMerge(e.RowIndex, e.ColumnIndex, e.Value);
            }

            IMergeCell cell = (IMergeCell)this[e.ColumnIndex, e.RowIndex];
            cellMg.IsHMergeNext = cell.IsHMergeNext;
            cellMg.IsHMergePrevious = cell.IsHMergePrevious;

            // let's owner draw handle
            if (cell.IsHMergeNext | cell.IsHMergePrevious)
            {
                e.Handled = true;
                this.OnMergeHorizontalPaint(e, cellMg);
            }
        }

        /// <summary>
        /// Merges the horizontal owner.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        private void MergeHorizontalOwner(DataGridViewCellPaintingEventArgs e)
        {
            if (this.CellHorizontalMerging != null)
            {
                CellMergeEventArgs cellMg;
                IMergeCellInternal cell;

                int runEnd = this.FirstDisplayedCell.ColumnIndex + this.DisplayedColumnCount(true);
                if (!((IMergeCellInternal)this[e.ColumnIndex, e.RowIndex]).IsAnalyzeMerge)
                {
                    for (int index = e.ColumnIndex; index < runEnd; index++)
                    {
                        if (this.ColumnsX[index].MergeDirection != MergeDirection.Horizontal)
                        {
                            break;
                        }

                        cellMg = new CellMergeEventArgs(index, e.RowIndex);

                        // The last row of grid
                        if (e.RowIndex == this.Rows.Count - 1)
                        {
                            cellMg.IsLastRow = true;
                        }

                        // The last row of grid
                        if (index == this.ColumnsX.Count - 1)
                        {
                            cellMg.IsLastColumn = true;
                        }

                        // fire an event
                        this.CellHorizontalMerging(this, cellMg);

                        // The first column is doesn't merge with any previous
                        if (cellMg.IsFirstColumn)
                        {
                            cellMg.IsHMergePrevious = false;
                        }

                        // The last column is doesn't merge with any next
                        if (cellMg.IsLastColumn)
                        {
                            cellMg.IsHMergeNext = false;
                        }

                        cell = (IMergeCellInternal)this[index, e.RowIndex];
                        cell.IsAnalyzeMerge = true;
                        cell.IsHMergeNext = cellMg.IsHMergeNext;
                        cell.IsHMergePrevious = cellMg.IsHMergePrevious;

                        if (cellMg.IsEndMerge)
                        {
                            break;
                        }
                    }
                }

                cell = (IMergeCellInternal)this[e.ColumnIndex, e.RowIndex];
                cellMg = new CellMergeEventArgs(e.ColumnIndex, e.RowIndex);
                cellMg.IsHMergeNext = cell.IsHMergeNext;
                cellMg.IsHMergePrevious = cell.IsHMergePrevious;

                // let's owner draw handle
                if (cellMg.IsHMergeNext | cellMg.IsHMergePrevious)
                {
                    e.Handled = true;
                    this.OnMergeHorizontalPaint(e, cellMg);
                }
            }
        }

        /// <summary>
        /// Create merge for both hori and vertical column.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        private void MergeBothContent(DataGridViewCellPaintingEventArgs e)
        {
            CellMergeEventArgs cellMg = new CellMergeEventArgs(e.ColumnIndex, e.RowIndex);

            // The last row of grid
            if (e.RowIndex == this.Rows.Count - 1)
            {
                cellMg.IsLastRow = true;
            }

            // The last row of grid
            if (e.ColumnIndex == this.ColumnsX.Count - 1)
            {
                cellMg.IsLastColumn = true;
            }

            // Analyze merge of previous & next columns & rows
            if (!(this[e.ColumnIndex, e.RowIndex] as IMergeCellInternal).IsAnalyzeMerge)
            {
                this.AnalyzeBothMerge(e.RowIndex, e.ColumnIndex, e.Value);
            }

            IMergeCell cell = (IMergeCell)this.Rows[e.RowIndex].Cells[e.ColumnIndex];
            cellMg.IsHMergeNext = cell.IsHMergeNext;
            cellMg.IsHMergePrevious = cell.IsHMergePrevious;
            cellMg.IsVMergeNext = cell.IsVMergeNext;
            cellMg.IsVMergePrevious = cell.IsVMergePrevious;

            // let's owner draw handle
            if (cellMg.IsVMergePrevious | cellMg.IsVMergeNext | cellMg.IsHMergePrevious | cellMg.IsHMergeNext)
            {
                e.Handled = true;
                this.OnMergeBothWayPaint(e, cellMg);
            }
        }

        /// <summary>
        /// Merges the both way owner.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        private void MergeBothOwner(DataGridViewCellPaintingEventArgs e)
        {
            if (this.CellBothMerging != null)
            {
                CellMergeEventArgs cellMg;
                IMergeCellInternal cell;

                int runRowEnd = this.FirstDisplayedCell.RowIndex + this.DisplayedRowCount(true);
                int runColEnd = this.FirstDisplayedCell.ColumnIndex + this.DisplayedColumnCount(true);
                if (!((IMergeCellInternal)this[e.ColumnIndex, e.RowIndex]).IsAnalyzeMerge)
                {
                    for (int indexRow = e.RowIndex; indexRow < runRowEnd; indexRow++)
                    {
                        for (int indexCol = e.ColumnIndex; indexCol < runColEnd; indexCol++)
                        {
                            if (this.ColumnsX[indexCol].MergeDirection != MergeDirection.Both)
                            {
                                break;
                            }

                            cellMg = new CellMergeEventArgs(indexCol, indexRow);

                            // The last row of grid
                            if (indexRow == this.Rows.Count - 1)
                            {
                                cellMg.IsLastRow = true;
                            }

                            // The last row of grid
                            if (indexCol == this.ColumnsX.Count - 1)
                            {
                                cellMg.IsLastColumn = true;
                            }

                            // fire an event
                            this.CellBothMerging(this, cellMg);

                            // The first row is doesn't merge with any previous
                            if (cellMg.IsFirstRow)
                            {
                                cellMg.IsVMergePrevious = false;
                            }

                            // The last row is doesn't merge with any next
                            if (cellMg.IsLastRow)
                            {
                                cellMg.IsVMergeNext = false;
                            }

                            // The first column is doesn't merge with any previous
                            if (cellMg.IsFirstColumn)
                            {
                                cellMg.IsHMergePrevious = false;
                            }

                            // The last column is doesn't merge with any next
                            if (cellMg.IsLastColumn)
                            {
                                cellMg.IsHMergeNext = false;
                            }

                            cell = (IMergeCellInternal)this[indexCol, indexRow];
                            cell.IsAnalyzeMerge = true;
                            cell.IsVMergeNext = cellMg.IsVMergeNext;
                            cell.IsVMergePrevious = cellMg.IsVMergePrevious;
                            cell.IsHMergeNext = cellMg.IsHMergeNext;
                            cell.IsHMergePrevious = cellMg.IsHMergePrevious;

                            if (cellMg.IsEndMerge)
                            {
                                break;
                            }
                        }
                    }
                }

                cell = (IMergeCellInternal)this[e.ColumnIndex, e.RowIndex];
                cellMg = new CellMergeEventArgs(e.ColumnIndex, e.RowIndex);
                cellMg.IsVMergeNext = cell.IsVMergeNext;
                cellMg.IsVMergePrevious = cell.IsVMergePrevious;
                cellMg.IsHMergeNext = cell.IsHMergeNext;
                cellMg.IsHMergePrevious = cell.IsHMergePrevious;

                // let's owner draw handle
                if (cellMg.IsVMergeNext | cellMg.IsVMergePrevious | cellMg.IsHMergePrevious | cellMg.IsHMergeNext)
                {
                    e.Handled = true;
                    this.OnMergeBothWayPaint(e, cellMg);
                }
            }
        }

        /// <summary>
        /// Fills the selection merge both cells.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        /// <param name="backClr">The backcolor.</param>
        /// <param name="mergeRows">The merge rows.</param>
        /// <param name="mergeCols">The merge cols.</param>
        private void FillSelectionMergeBothCells(DataGridViewCellPaintingEventArgs e, SolidBrush backClr, List<int> mergeRows, List<int> mergeCols)
        {
            mergeRows.ForEach(delegate(int indexRow)
            {
                mergeCols.ForEach(delegate(int indexCol)
                {
                    if (this[indexCol, indexRow].Selected)
                    {
                        backClr.Color = this[indexCol, indexRow].InheritedStyle.SelectionBackColor;
                        e.Graphics.FillRectangle(backClr, this.GetCellDisplayRectangle(indexCol, indexRow, false));
                        return;
                    }
                });
            });
        }

        /// <summary>
        /// Fills the selection merge horizontal cells.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        /// <param name="backClr">The backcolor.</param>
        /// <param name="mergeCols">The merge cols.</param>
        private void FillSelectionMergeHorizontalCells(DataGridViewCellPaintingEventArgs e, SolidBrush backClr, List<int> mergeCols)
        {
            mergeCols.ForEach(delegate(int index)
            {
                if (this[index, e.RowIndex].Selected)
                {
                    backClr.Color = this[index, e.RowIndex].InheritedStyle.SelectionBackColor;
                    e.Graphics.FillRectangle(backClr, this.GetCellDisplayRectangle(index, e.RowIndex, false));
                    return;
                }
            });
        }

        /// <summary>
        /// Fills the merge vertical cells.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellPaintingEventArgs"/> instance containing the event data.</param>
        /// <param name="backClr">The backcolor.</param>
        /// <param name="mergeRows">The merge rows.</param>
        private void FillSelectionMergeVerticalCells(DataGridViewCellPaintingEventArgs e, SolidBrush backClr, List<int> mergeRows)
        {
            foreach (int index in mergeRows)
            {
                if (this[e.ColumnIndex, index].Selected)
                {
                    backClr.Color = this[e.ColumnIndex, index].InheritedStyle.SelectionBackColor;
                    Rectangle selectBound = this.GetCellDisplayRectangle(e.ColumnIndex, index, false);
                    if (e.ColumnIndex == 0)
                    {
                        selectBound.X--;
                        selectBound.Width++;
                    }

                    e.Graphics.FillRectangle(backClr, selectBound);
                }
            }
        }

        /// <summary>
        /// Set the merge cell value.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        /// <param name="mergeCol">The merge col.</param>
        private void SetMergeCellValue(DataGridViewCellEventArgs e, MergeGridViewColumn mergeCol)
        {
            if (mergeCol.MergeDirection != MergeDirection.None)
            {
                if (mergeCol.MergeDirection == MergeDirection.Vertical)
                {
                    this.SetVerticalCellsValue(e);
                }
                else if (mergeCol.MergeDirection == MergeDirection.Horizontal)
                {
                    this.SetHorizontalCellsValue(e);
                }
                else if (mergeCol.MergeDirection == MergeDirection.Both)
                {
                    this.SetBothWayCellsValue(e);
                }
            }
        }

        /// <summary>
        /// Set the vertical cells value.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void SetVerticalCellsValue(DataGridViewCellEventArgs e)
        {
            object value = this[e.ColumnIndex, e.RowIndex].Value;
            List<int> mergeRows = this.FindMergeRowsDisplayIndex(e.RowIndex, e.ColumnIndex);
            if (mergeRows != null)
            {
                // The previous of end cells block
                if (mergeRows[0] > 0)
                {
                    ((IMergeCellInternal)this[e.ColumnIndex, mergeRows[0] - 1]).IsAnalyzeMerge = false;
                }

                // Pass the begin element of array : because itself
                for (int index = 0; index < mergeRows.Count; index++)
                {
                    this[e.ColumnIndex, mergeRows[index]].Value = value;
                    ((IMergeCellInternal)this[e.ColumnIndex, mergeRows[index]]).IsAnalyzeMerge = false;
                }

                // The next of end cells block
                if (mergeRows[mergeRows.Count - 1] < this.Rows.Count - 1)
                {
                    ((IMergeCellInternal)this[e.ColumnIndex, mergeRows[mergeRows.Count - 1] + 1]).IsAnalyzeMerge = false;
                }
            }
            else
            {
                if (e.RowIndex > 0)
                {
                    ((IMergeCellInternal)this[e.ColumnIndex, e.RowIndex - 1]).IsAnalyzeMerge = false;
                }

                if (e.RowIndex < this.Rows.Count - 1)
                {
                    ((IMergeCellInternal)this[e.ColumnIndex, e.RowIndex + 1]).IsAnalyzeMerge = false;
                }
            }
        }

        /// <summary>
        /// Set the horizontal cells value.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void SetHorizontalCellsValue(DataGridViewCellEventArgs e)
        {
            object value = this[e.ColumnIndex, e.RowIndex].Value;
            List<int> mergeColumns = this.FindMergeColumnsIndex(e.RowIndex, e.ColumnIndex);
            if (mergeColumns != null)
            {
                // The previous of end cells block
                if (mergeColumns[0] > 0)
                {
                    ((IMergeCellInternal)this[mergeColumns[0] - 1, e.RowIndex]).IsAnalyzeMerge = false;
                }

                // Pass the begin element of array : because itself
                for (int index = 0; index < mergeColumns.Count; index++)
                {
                    this[mergeColumns[index], e.RowIndex].Value = value;
                    ((IMergeCellInternal)this[mergeColumns[index], e.RowIndex]).IsAnalyzeMerge = false;
                }

                // The next of end cells block
                if (mergeColumns[mergeColumns.Count - 1] < this.Rows.Count - 1)
                {
                    ((IMergeCellInternal)this[mergeColumns[mergeColumns.Count - 1] + 1, e.RowIndex]).IsAnalyzeMerge = false;
                }
            }
            else
            {
                if (e.ColumnIndex > 0)
                {
                    ((IMergeCellInternal)this[e.ColumnIndex - 1, e.RowIndex]).IsAnalyzeMerge = false;
                }

                if (e.ColumnIndex < this.Columns.Count - 1)
                {
                    ((IMergeCellInternal)this[e.ColumnIndex + 1, e.RowIndex]).IsAnalyzeMerge = false;
                }
            }
        }

        /// <summary>
        /// Sets the both way cells value.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void SetBothWayCellsValue(DataGridViewCellEventArgs e)
        {
            object value = this.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
            ((IMergeCellInternal)this[e.ColumnIndex, e.RowIndex]).IsAnalyzeMerge = false;
            List<int> mergeColumns = this.FindMergeColumnsIndex(e.RowIndex, e.ColumnIndex);
            List<int> mergeRows = this.FindMergeRowsDisplayIndex(e.RowIndex, e.ColumnIndex);

            if (mergeColumns != null && mergeRows != null)
            {
                for (int indexRow = 0; indexRow < mergeRows.Count; indexRow++)
                {
                    for (int indexCol = 0; indexCol < mergeColumns.Count; indexCol++)
                    {
                        this[mergeColumns[indexCol], mergeRows[indexRow]].Value = value;
                        ((IMergeCellInternal)this[mergeColumns[indexCol], mergeRows[indexRow]]).IsAnalyzeMerge = false;
                    }
                }
            }
            else if (mergeColumns != null && mergeRows == null)
            {
                for (int index = 0; index < mergeColumns.Count; index++)
                {
                    this[mergeColumns[index], e.RowIndex].Value = value;
                    ((IMergeCellInternal)this[mergeColumns[index], e.RowIndex]).IsAnalyzeMerge = false;
                }
            }
            else if (mergeRows != null && mergeColumns == null)
            {
                for (int index = 0; index < mergeRows.Count; index++)
                {
                    this[e.ColumnIndex, mergeRows[index]].Value = value;
                    ((IMergeCellInternal)this[e.ColumnIndex, mergeRows[index]]).IsAnalyzeMerge = false;
                }
            }
        }
    }
}